home *** CD-ROM | disk | FTP | other *** search
/ CU Amiga Super CD-ROM 19 / CU Amiga Magazine's Super CD-ROM 19 (1998)(EMAP Images)(GB)[!][issue 1998-02].iso / CUCD / Online / NNTPd / server / batch.c < prev    next >
Encoding:
C/C++ Source or Header  |  2002-12-09  |  12.5 KB  |  534 lines

  1. #ifndef lint
  2. static    char    rcsid[] = "@(#)$Id: batch.c,v 1.21 1994/12/09 02:52:18 sob Exp sob $";
  3. #endif
  4. /*
  5.  * Batch subroutine for Cnews.
  6.  * Cooperates with C news input subsystem.
  7.  *    newsboot must be told to run partial batches left at a crash.
  8.  *
  9. ***************************************************************************
  10. This work in its current form is Copyright 1989 Stan Barber
  11. and is based on the work of Henry Spencer and Geoff Collyer at the University
  12. of Toronto. This software may be distributed freely as long as no profit is
  13. made from such distribution and this notice is reproducted in whole.
  14. ***************************************************************************
  15. This software is provided on an "as is" basis with no guarantee of 
  16. usefulness or correctness of operation for any purpose, intended or
  17. otherwise. The author is in no way liable for this software's performance
  18. or any damage it may cause to any data of any kind anywhere.
  19. ***************************************************************************
  20. */
  21. #include "common.h"
  22. #include <signal.h>
  23. #ifdef sparc
  24. #ifndef SVR4
  25. #include <vfork.h>
  26. #endif
  27. #endif
  28. #ifdef BATCHED_INPUT
  29. #define YES 1
  30. #define NO 0
  31.  
  32. /* imports */
  33. extern time_t time();
  34. extern char *malloc(), *mktemp(), *index(), *rindex();
  35. /* forwards */
  36. static char *strsave();
  37. #ifdef XFER_TIMEOUT
  38. static SIGRET xfer_timeout();
  39. #endif
  40. static int cpstdin();
  41. static int appbatch();
  42. static int enqueue();
  43.  
  44. /* private data */
  45. static char tempfile[256];
  46. static int xfer_lines, old_xfer_lines;
  47.  
  48. static char art[COPYSIZE];        /* entire article, if it fits */
  49. static char *endart = art;        /* points just past end of article */
  50. static int incore = YES;
  51.  
  52. #ifdef NONEWSRUN
  53. static int uniq = 0;                    /* unique counter for this process */
  54. static int in_batchdir = NO;
  55.  
  56. #endif /* NONEWSRUN */
  57. static struct batch_file {
  58.     char *name;
  59.     FILE *file;
  60.     char isopen;
  61.     time_t start;            /* time of creation */
  62.     off_t size;            /* current size */
  63.     int arts;            /* number of articles */
  64. } btch = { NULL, NULL, NO, 0, 0 };
  65.  
  66. /*
  67.  * stash stdin (up to ".") on the end of the batch input file.
  68.  * kick newsrun if the batch is non-empty and too big or too old.
  69.  *
  70.  * Parameters:
  71.  *    "cont_code" is the response code to transmit on successful startup.
  72.  *    "err_code" is the response code to transmit when something goes wrong.
  73.  *
  74.  * Returns: -1 on non-zero return from child, 0 on error before fork/exec, 1 else.
  75.  * Side effects: Creates and removes temporary file; accepts input from client.
  76.  *        Can time out if XFER_TIMEOUT is defined.
  77.  */
  78. int
  79. batch_input_article(cont_code, err_code, errbuf, msg_id)
  80. int cont_code, err_code;
  81. char *errbuf;
  82. char *msg_id;
  83. {
  84.     int status = 1;            /* okay status */
  85.  
  86.     /* protect locking */
  87.     signal(SIGINT, SIG_IGN);
  88.     signal(SIGQUIT, SIG_IGN);
  89.     signal(SIGHUP, SIG_IGN);
  90.  
  91. #ifdef NONEWSRUN
  92.     /* This really should be done in main.c so that we don't have to
  93.        check each time we get a new article - sigh */
  94.  
  95.     if (!in_batchdir)
  96.         if (chdir(INDIR) < 0)
  97.             syslog(LOG_ERR, "chdir(%s) failed", INDIR);
  98.         else
  99.             in_batchdir = YES;
  100.  
  101. #endif /* NONEWSRUN */
  102.     if (btch.name == NULL) {
  103.         /* BATCH_FILE may trigger unprivileged() */
  104.         btch.name = mktemp(strsave(BATCH_FILE));
  105.     }
  106.     if (btch.name == NULL)
  107.         return 0;
  108.     tempfile[0] = '\0';
  109. #ifdef UMASK
  110.     (void) umask(UMASK);
  111. #endif
  112.     /* may create tempfile */
  113.     if (!cpstdin(cont_code, err_code, errbuf, msg_id))
  114.         return 0;
  115. #ifdef POSTER
  116.     if (tempfile[0])
  117.         (void) chown(tempfile, uid_poster, gid_poster);
  118. #endif
  119.     status = appbatch();
  120.     btch.arts++;
  121.     if (tempfile[0] != '\0')
  122.         (void) unlink(tempfile);
  123.     if (status == 1 && oktorunbatch())
  124.         status = enqueue(cont_code, err_code, errbuf);
  125.     return status;
  126. }
  127.  
  128. int                        /* boolean */
  129. oktorunbatch()
  130. {
  131.     struct stat stbuf;
  132.  
  133.     if (!btch.isopen || fstat(fileno(btch.file), &stbuf) < 0)
  134.         return NO;
  135.     btch.size = stbuf.st_size;
  136.     return(btch.arts >= TOOMANY || btch.size > TOOBIG ||
  137.         btch.size > 0 && time((time_t *)NULL) - btch.start > TOOOLD);
  138. }
  139.  
  140. /*
  141.  * Copy standard input (up to a "." line) to art, if it fits,
  142.  * else to a temporary file.
  143.  */
  144. /* ARGSUSED errbuf */
  145. static int                    /* boolean: got article ok? */
  146. cpstdin(cont_code, err_code, errbuf, msg_id)
  147. int cont_code, err_code;
  148. char *errbuf, *msg_id;
  149. {
  150.     register FILE *tfp = NULL;
  151.     register char *cp, *realline;
  152.     char line[NNTP_STRLEN];
  153.     int toobig = NO;
  154.  
  155.     /* TODO: is this right?  used to open here, with errors here */
  156.     printf("%d Ok\r\n", cont_code);
  157.     (void) fflush(stdout);
  158.  
  159.     xfer_lines = old_xfer_lines = 0;
  160.     incore = YES;
  161.     art[0] = '\0';
  162.     endart = art;
  163. #ifdef XFER_TIMEOUT
  164.     signal(SIGALRM, xfer_timeout);
  165.     (void) alarm(XFER_TIMEOUT);
  166. #endif
  167.     cp = line; /* set it to anything non-NULL as a flag */
  168.     while (fgets(line, sizeof line, stdin) != NULL) {
  169.         xfer_lines++;
  170.         if (cp != NULL && line[0] == '.'
  171.          && (line[1] == '\r' || line[1] == '\n' || line[1] == '\0'))
  172.             break;                /* article end: exit */
  173.         if ((cp = rindex(line, '\r')) != NULL ||
  174.             (cp = rindex(line, '\n')) != NULL)
  175.             *cp = '\0';            /* nuke CRLF */
  176.         /* remove hidden dot if present */
  177.         realline = (line[0] == '.'? line+1: line);
  178.         if (toobig) {                /* straight to disk */
  179.             (void) fputs(realline, tfp);
  180.             if (cp != NULL)
  181.                 (void) putc('\n', tfp);
  182.         } else {
  183.             int len = strlen(realline);
  184.  
  185.             /*
  186.              * Does art have room to append realline + \n\0?
  187.              * If not, open temp file and dump art & realline there.
  188.              */
  189.             if (sizeof art - (endart - art) < len + 1 + 1) {
  190.                 (void) strcpy(tempfile, "/tmp/rpostXXXXXX");
  191.                 (void) mktemp(tempfile);
  192.                 tfp = fopen(tempfile, "w");
  193.                 if (tfp == NULL) {
  194.                     printf("%d Cannot create temporary file.\r\n",
  195.                         err_code);
  196.                     (void) fflush(stdout);
  197.                     return 0;
  198.                 }
  199. #ifdef OK_IN_MIDDLE_OKAY
  200.                 else {
  201.                     printf("%d Ok\r\n", cont_code);
  202.                     (void) fflush(stdout);
  203.                 }
  204. #endif
  205.                 (void) fwrite(art, 1, endart - art, tfp);
  206.                 toobig = YES;
  207.                 incore = NO;
  208.                 art[0] = '\0';
  209.                 endart = art;
  210.                 (void) fputs(realline, tfp);
  211.                 if (cp != NULL)
  212.                     (void) putc('\n', tfp);
  213.             } else {
  214.                 /* fits: append realline\n to art at endart */
  215.                 (void) strcpy(endart, realline);
  216.                 endart += len;
  217.                 if (cp != NULL)
  218.                     *endart++ = '\n';
  219.                 *endart = '\0';
  220.             }
  221.         }
  222.     }
  223.     if (tfp != NULL)
  224.         (void) fclose(tfp);
  225. #ifdef XFER_TIMEOUT
  226.     (void) alarm(0);
  227.     (void) signal(SIGALRM, SIG_DFL);
  228. #endif
  229.  
  230.     /* See if the connection got closed somehow... */
  231.     if (line[0] != '.' && line[1] != '\0') {
  232.         if (tempfile[0] != '\0')
  233.             (void) unlink(tempfile);
  234. #ifdef SYSLOG
  235. #ifdef LOG
  236.         syslog(LOG_ERR,
  237.             "%s cpstdin: EOF before period on line by itself %s",
  238.             hostname, msg_id);
  239. #else
  240.         syslog(LOG_ERR,
  241.             "cpstdin: EOF before period on line by itself %s", msg_id);
  242. #endif
  243. #endif
  244.         return 0;
  245.     }
  246.     return 1;
  247. }
  248.  
  249. static SIGRET
  250. xfer_timeout()
  251. {
  252. #ifdef XFER_TIMEOUT
  253.     if (old_xfer_lines < xfer_lines) {
  254.         old_xfer_lines = xfer_lines;
  255.         (void) signal(SIGALRM, xfer_timeout);
  256.         (void) alarm(XFER_TIMEOUT);
  257. #ifdef VOIDSIG
  258.         return;
  259. #else
  260.         return 0;
  261. #endif
  262.     }
  263.     /* Timed out. */
  264.     printf("%d timeout after %d seconds, closing connection.\r\n",
  265.         ERR_FAULT, XFER_TIMEOUT);
  266.     fflush(stdout);
  267. #ifdef SYSLOG
  268. #ifdef LOG
  269.     syslog(LOG_ERR, "%s transfer_timeout", hostname);
  270. #endif /* LOG */
  271. #endif
  272.     (void) unlink(tempfile);
  273.     exit(1);
  274. #endif /* XFER_TIMEOUT */
  275. }
  276.  
  277. /*
  278.  * Append "#! rnews count" and art (or tempfile) to batch file, locking
  279.  * assumed. If batch file is too big or too old (but not empty), feed it
  280.  * to newsrun.
  281.  */
  282. static int                /* same as batch_input_article */
  283. appbatch()
  284. {
  285.     register FILE *tfp = NULL;
  286.     register int bytes = 0;
  287.     int status = 1;                /* okay status */
  288.     long size = 0;
  289.     char artbuf[COPYSIZE];
  290.     struct stat stbuf;
  291.  
  292.     if (btch.file == NULL) {
  293.         btch.file = fopen(btch.name, "a");
  294.         if (btch.file == NULL) {
  295. #ifdef SYSLOG
  296.             syslog(LOG_ERR,"appbatch: fopen: %s: %m", btch.name);
  297. #endif
  298.             return 0;
  299.         }
  300.         btch.isopen = YES;
  301.         btch.start = time(&btch.start);
  302.         btch.size = 0;
  303.         btch.arts = 0;
  304.     }
  305.  
  306.     /* find article size and write the article */
  307.     if (incore)
  308.         size = endart - art;
  309.     else {
  310.         tfp = fopen(tempfile, "r");
  311.         if (tfp == NULL)
  312.             return 0;
  313.         if (fstat(fileno(tfp), &stbuf) >= 0)
  314.             size = stbuf.st_size;
  315.     }
  316.     (void) fprintf(btch.file, "#! rnews %ld %s\n", size, hostname);
  317.  
  318.     /* copy the article to the batch file */
  319.     if (incore)
  320.         (void) fwrite(art, 1, endart - art, btch.file);
  321.     else {
  322.         while ((bytes = fread(artbuf, 1, sizeof artbuf, tfp)) > 0)
  323.             if (fwrite(artbuf, 1, bytes, btch.file) != bytes) {
  324. #ifdef SYSLOG
  325.                 syslog(LOG_ERR,
  326.                     "enqueue: fwrite can't write %s",
  327.                     btch.name);
  328. #endif
  329.                 status = 0;    /* hmm, #! count is off */
  330.                 break;
  331.             }
  332.         (void) fclose(tfp);
  333.     }
  334.     if (fflush(btch.file) == EOF) {
  335. #ifdef SYSLOG
  336.         syslog(LOG_ERR, "enqueue: fflush: %s", btch.name);
  337. #endif
  338.         status = 0;
  339.     }
  340.     return status;
  341. }
  342.  
  343. /*
  344.  * Enqueue any partial batch.  Called before exit.
  345.  */
  346. void
  347. enqpartbatch(cont_code, err_code, errbuf)
  348. int cont_code, err_code;
  349. char *errbuf;
  350. {
  351.     struct stat stbuf;
  352.  
  353.     if (btch.isopen && fstat(fileno(btch.file), &stbuf) >= 0) {
  354.         if (btch.size > 0)
  355.             enqueue(cont_code, err_code, errbuf);
  356.         else {
  357.             (void) fclose(btch.file);
  358.             btch.file = NULL;
  359.             btch.isopen = NO;
  360.             (void) unlink(btch.name); /* remove empty batch */
  361.         }
  362.     }
  363. }
  364.  
  365. /* 
  366.  * insert the batch file into the input subsystem queue by renaming
  367.  * it to an all-numeric name, then kick newsrun to process it.
  368.  * locks btch.name as appropriate.
  369.  */
  370. static int                /* same as batch_input_article */
  371. enqueue(cont_code, err_code, errbuf)
  372. int cont_code, err_code;
  373. char *errbuf;
  374. {
  375.     time_t now;
  376.     int pid, wpid, fd, exitstat;
  377. #if defined(USG) || defined(BSD_44)
  378.     int status;
  379. #else
  380.     union wait status;
  381. #endif
  382.     char permname[MAXDIGITS], *number = permname, *newsrun;
  383.     struct stat stbuf;
  384. #ifdef POSTER
  385.     char *envp[4], user[sizeof(POSTER) + 5], logname[sizeof(POSTER) + 8];
  386.     char *home;
  387. #else
  388.     char *envp[1];
  389. #endif
  390.  
  391.     if (fclose(btch.file) != 0) {
  392. #ifdef SYSLOG
  393.         syslog(LOG_ERR, "enqueue: fclose: %m");
  394. #endif
  395.     }
  396.     btch.file = NULL;
  397.     btch.isopen = NO;
  398.     btch.start = 0;
  399.     btch.size = 0;
  400.     btch.arts = 0;
  401.  
  402.     (void) fflush(stdout);
  403.     (void) fflush(stderr);
  404. #ifndef NONEWSRUN
  405.     pid = fork();
  406.     if (pid == -1) {
  407. #ifdef SYSLOG
  408.         syslog(LOG_ERR, "enqueue: fork: %m");
  409. #endif
  410.         return 0;
  411.     } else if (pid != 0) {            /* parent */
  412.         while ((wpid = wait(&status)) != -1 && wpid != pid)
  413.             ;
  414. #if defined(USG) || defined(BSD_44)
  415.         exitstat = (status >> 8) & 0xff;
  416. #else
  417.         exitstat = status.w_T.w_Retcode;
  418. #endif
  419. #ifdef SYSLOG
  420.         if (exitstat != 0) {
  421.             syslog(LOG_ERR, " enqueue returned exit status 0%o",
  422.                 exitstat);
  423.         }
  424. #endif
  425.         return exitstat != 0? -1 :1;
  426.     }
  427. #endif /* NONEWSRUN */
  428. #ifdef POSTER
  429. #ifndef USG
  430.         if (getuid() == 0) initgroups(POSTER,gid_poster);
  431. #endif
  432.         (void) setgid(gid_poster);
  433.         (void) setuid(uid_poster);
  434. #endif
  435. #ifndef NONEWSRUN
  436.     /* child: must exit */
  437. #ifdef SYSLOG
  438.     /* Close in such a way that syslog() will know to reopen */
  439.     closelog();
  440. #endif
  441.     for (fd = 3; fd < 20; fd++)
  442.         (void) close(fd);
  443.     if (chdir(INDIR) < 0) {
  444. #ifdef SYSLOG
  445.         syslog(LOG_ERR, "enqueue: chdir(%s): %m", INDIR);
  446. #else
  447.         ;
  448. #endif
  449.     }
  450.  
  451.     /* rename btch.name to a number so newsrun will see it */
  452. # ifdef CNEWS_CLEARTEXT
  453.     /* .t indicates a known-text file to newsrun */
  454.     sprintf(number, "%ld.t", (long)time(&now));
  455. # else
  456.     sprintf(number, "%ld", (long)time(&now));
  457. # endif
  458. #else
  459.     sprintf(number, "%d.%d", getpid(), uniq++);
  460. #endif /* NONEWSRUN */
  461.     while (link(btch.name, permname) < 0) {
  462.         if (stat(btch.name, &stbuf) < 0)
  463.             break;
  464.         sleep(2);
  465. #ifndef NONEWSRUN
  466. # ifdef CNEWS_CLEARTEXT
  467.         sprintf(number, "%ld.t", (long)time(&now));
  468. # else
  469.         sprintf(number, "%ld", (long)time(&now));
  470. # endif
  471. #else
  472.         sprintf(number, "%d.%d", getpid(), uniq++);
  473. #endif /* NONEWSRUN */
  474.     }
  475.     if (unlink(btch.name) < 0) {
  476. #ifdef SYSLOG
  477.         syslog(LOG_ERR, "enqueue: cannot unlink %s: %m", btch.name);
  478. #endif
  479.     }
  480. #ifndef NONEWSRUN
  481.  
  482.     signal(SIGINT, SIG_IGN);
  483.     signal(SIGQUIT, SIG_IGN);
  484.     signal(SIGHUP, SIG_IGN);
  485.     (void) fflush(stdout);
  486.     (void) fflush(stderr);
  487.     newsrun = strsave(NEWSRUN);
  488.     if (newsrun == NULL)
  489.         newsrun = NEWSRUN;
  490.  
  491.     /* Empty environment keeps cnews inews from telling lies */
  492. #ifdef POSTER
  493.     sprintf(user, "USER=%s", POSTER);
  494.     sprintf(logname, "LOGNAME=%s", POSTER);
  495.     if ((home = (char *)malloc(strlen(home_poster)+5+1)) != NULL)
  496.         sprintf(home, "HOME=%s", home_poster);
  497.     envp[0] = user;
  498.     envp[1] = logname;
  499.     envp[2] = home;
  500.     envp[3] = 0;
  501. #else
  502.     envp[0] = 0;
  503. #endif
  504. #ifdef USG
  505.      /* execle() fails because newsrun is a shell procedure */
  506.      execle("/bin/sh", "sh", newsrun, (char *)NULL, envp);
  507. #else
  508.     execle(newsrun, newsrun, (char *)NULL, envp);
  509. #endif
  510. #ifdef SYSLOG
  511.     syslog(LOG_ERR, "enqueue: execle(%s): %m", newsrun);
  512. #endif
  513.     exit(1);
  514.     /* NOTREACHED */
  515. #else
  516.     return(1);
  517. #endif /* NONEWSRUN */
  518. }
  519.  
  520. static char *
  521. strsave(s)
  522. register char *s;
  523. {
  524.     register char *news = malloc((unsigned)(strlen(s) + 1));
  525.  
  526.     if (news != NULL)
  527.         strcpy(news, s);
  528.     return news;
  529. }
  530. #endif
  531.  
  532.  
  533.  
  534.